package com.flying.carclub.http;

import android.annotation.SuppressLint;
import android.content.Context;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Message;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;

import com.flying.carclub.MyApplication;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
import javax.net.ssl.X509TrustManager;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.FormBody;
import okhttp3.Headers;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;


/**
 * Created by xcheng on 2015/9/16.
 */
public class OkHttpExecutor implements IOkHttpApi {

    private final static String TAG = "https";
    public static final MediaType MEDIA_TYPE_MARKDOWN = MediaType.parse("text/x-markdown; charset=utf-8");

    public static OkHttpClient mClient;
    public static OkHttpExecutor instance = new OkHttpExecutor();

    /**
     * 不进行主机名确认
     */
    private static class TrustAnyHostnameVerifier implements HostnameVerifier {
        public boolean verify(String hostname, SSLSession session) {
            return true;
        }
    }

    /**
     * 信任所有主机 对于任何证书都不做SSL检测
     * 安全验证机制，而Android采用的是X509验证
     */
    private static class MyX509TrustManager implements X509TrustManager {

        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }

        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }

        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }
    }

    private OkHttpExecutor() {
        if (mClient == null) {
            mClient = new OkHttpClient.Builder()
//                        .sslSocketFactory(sc.getSocketFactory())
                    .connectTimeout(OkRequestParams.DEFAULT_TIME_OUT, TimeUnit.MILLISECONDS)
                    .writeTimeout(OkRequestParams.DEFAULT_TIME_OUT, TimeUnit.MILLISECONDS)
                    .readTimeout(OkRequestParams.DEFAULT_TIME_OUT, TimeUnit.MILLISECONDS)
                    .retryOnConnectionFailure(true)
                    .build();
        }
    }


    /**
     * construct Request with params and params;
     *
     * @param url
     * @param valuePairs
     * @return
     */
    @Override
    public Request buildHttpGetRequest(String url, List<NameValuePair> valuePairs) {
        String uri = buildQueryUrl(url,valuePairs);
        Log.d(TAG, "HttpGet:" +uri);
        return new Request.Builder().url(uri).build();
    }

    @Override
    public Request buildHttpPostRequest(String url, List<NameValuePair> valuePairs) {
        FormBody.Builder formBuilder = new FormBody.Builder();
        for (NameValuePair nameValuePair : valuePairs) {
//            formBuilder.add(Uri.encode(nameValuePair.getName(), "UTF-8"), Uri.encode(nameValuePair.getValue(),"UTF-8"));
            formBuilder.add(nameValuePair.getName(), nameValuePair.getValue());
        }
        RequestBody formBody = formBuilder.build();
        Log.d(TAG, "HttpPost:" + url + "," + valuePairs.toString());
        return new Request.Builder().url(url).post(formBody).headers(addHttpHeader()).build();
    }

    @Override
    public Request buildHttpPostRequest(String url, List<NameValuePair> valuePairs, String cookie) {
        FormBody.Builder formBuilder = new FormBody.Builder();
        if (valuePairs!=null) {
            for (NameValuePair nameValuePair : valuePairs) {
                formBuilder.add(nameValuePair.getName(), nameValuePair.getValue());
            }
        }
        Log.d(TAG, "HttpPost:" + url + "," + (valuePairs==null?"":valuePairs.toString()));
        RequestBody formBody = formBuilder.build();
        if (TextUtils.isEmpty(cookie)) {
            return new Request.Builder().url(url).post(formBody).build();
        }
        return new Request.Builder().url(url).post(formBody).headers(addHttpHeader(cookie)).build();
    }

    @Override
    public Request buildHttpGetRequest(OkRequestParams params) {
        FormBody.Builder formBuilder = new FormBody.Builder();
        List<NameValuePair> valuePairs = params.getParams();
        if (valuePairs!=null) {
            for (NameValuePair nameValuePair : valuePairs) {
                formBuilder.add(nameValuePair.getName(),nameValuePair.getValue());
            }
        }

        String url = buildQueryUrl(params);
        Log.d(TAG, "HttpGet:" + url);
        return new Request.Builder().url(url).headers(addHttpHeader(params.getHeaders())).build();
    }

    @Override
    public Request buildHttpPostRequest(OkRequestParams params) {
        FormBody.Builder formBuilder = new FormBody.Builder();
        List<NameValuePair> valuePairs = params.getParams();
        if (valuePairs!=null) {
            for (NameValuePair nameValuePair : valuePairs) {
//                formBuilder.add(Uri.encode(nameValuePair.getName(), params.getCharset()), Uri.encode(nameValuePair.getValue(),params.getCharset()));
                formBuilder.add(nameValuePair.getName(), nameValuePair.getValue());
            }
        }
        Log.d(TAG, "HttpPost:" + params.getUrl() + "," + (valuePairs==null?"":valuePairs.toString()));
        RequestBody formBody = formBuilder.build();
        return new Request.Builder().url(params.getUrl()).post(formBody).headers(addHttpHeaders(params.getHeaders())).build();
    }

    @Override
    public Request buildHttpPostStringRequest(OkRequestParams params) {
        RequestBody requestBody = RequestBody.create(MEDIA_TYPE_MARKDOWN,params.getData());
        Log.d(TAG, "HttpPostString:" + params.getUrl());
        return new Request.Builder().url(params.getUrl()).post(requestBody).headers(addHttpHeaders(params.getHeaders())).build();
    }

    @Override
    public Request buildHttpPostFileRequest(OkRequestParams params) {
        RequestBody requestBody = RequestBody.create(MEDIA_TYPE_MARKDOWN,params.getFile());
        Log.d(TAG, "HttpPostString:" + params.getUrl());
        return new Request.Builder().url(params.getUrl()).post(requestBody).headers(addHttpHeaders(params.getHeaders())).build();
    }


    /**
     * 执行网络请求
     *
     * @param handleParams
     * @throws Exception
     */
    @Override
    public void executeHttpRequest(final ApiHandleParams handleParams) throws Exception {
        if (NetworkUtil.isNetWorkActive(MyApplication.getInstance())) {
            Request request = handleParams.getRequest();
            Call call;
            if (handleParams.getConnectTimeOut() == OkRequestParams.DEFAULT_TIME_OUT){
                call = mClient.newCall(request.newBuilder().tag(handleParams.getToken()).build());
            }else {
                OkHttpClient okHttpClient = mClient.newBuilder()
                        .connectTimeout(handleParams.getConnectTimeOut(), TimeUnit.MILLISECONDS)
                        .readTimeout(handleParams.getConnectTimeOut(), TimeUnit.MILLISECONDS).build();
                call = okHttpClient.newCall(request.newBuilder().tag(handleParams.getToken()).build());
            }
            call.enqueue(new Callback() {

                @Override
                public void onFailure(Call call, IOException e) {
                    if (call.isCanceled()) {
                        handleParams.setOkHttpStatus(OkHttpStatus.CANCLE);
                    } else if (e.getClass().equals(SocketTimeoutException.class)) {
                        handleParams.setOkHttpStatus(OkHttpStatus.REQUEST_TIMEOUT);
                    } else {
                        e.printStackTrace();
                        handleParams.setOkHttpStatus(OkHttpStatus.OTHER_HTTPERROR);
                    }
                    notifyMainThread(handleParams);
                }

                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    if (OkHttpStatus.CANCLE != handleParams.getOkHttpStatus()) {
                        handleResponse(response, handleParams);
                    }
                    onExecuteFinished(handleParams);
                    notifyMainThread(handleParams);
                }

            });
        } else {
            handleParams.setOkHttpStatus(OkHttpStatus.NO_NETWORK);
            notifyMainThread(handleParams);
        }
    }
    @Override
    public String executeHttpSyncRequest(ApiHandleParams handleParams) throws Exception {
        if (NetworkUtil.isNetWorkActive(MyApplication.getInstance())) {
            Request request = handleParams.getRequest();
            Call call;
            if (handleParams.getConnectTimeOut()==OkRequestParams.DEFAULT_TIME_OUT){
                call = mClient.newCall(request.newBuilder().tag(handleParams.getToken()).build());
            }else {
                OkHttpClient okHttpClient = mClient.newBuilder()
                        .connectTimeout(handleParams.getConnectTimeOut(),TimeUnit.MILLISECONDS)
                        .readTimeout(handleParams.getConnectTimeOut(),TimeUnit.MILLISECONDS).build();
                call = okHttpClient.newCall(request.newBuilder().tag(handleParams.getToken()).build());
            }

            Response response =  call.execute();
            if (response.isSuccessful()){
                return response.body().string();
            }
        } else {
            handleParams.setOkHttpStatus(OkHttpStatus.NO_NETWORK);
        }
        return null;
    }
    @Override
    public Response executeHttpSyncRequestReturnResponse(ApiHandleParams handleParams) throws Exception {
        if (NetworkUtil.isNetWorkActive(MyApplication.getInstance())) {
            Request request = handleParams.getRequest();
            Call call;
            if (handleParams.getConnectTimeOut()==OkRequestParams.DEFAULT_TIME_OUT){
                call = mClient.newCall(request.newBuilder().tag(handleParams.getToken()).build());
            }else {
                OkHttpClient okHttpClient = mClient.newBuilder()
                        .connectTimeout(handleParams.getConnectTimeOut(),TimeUnit.MILLISECONDS)
                        .readTimeout(handleParams.getConnectTimeOut(),TimeUnit.MILLISECONDS).build();
                call = okHttpClient.newCall(request.newBuilder().tag(handleParams.getToken()).build());
            }
            Response response =  call.execute();
            if (response.isSuccessful()){
                return response;
            }
        } else {
            handleParams.setOkHttpStatus(OkHttpStatus.NO_NETWORK);
        }
        return null;
    }

    /**
     * 处理返回数据的逻辑
     *
     * @param response
     * @param handleParams
     */
    private void handleResponse(Response response, ApiHandleParams handleParams) {
        try {
            handleParams.setResponse(response);
            int statusCode = response.code();
            String msg = response.body().string();
            switch (statusCode) {
                case HttpURLConnection.HTTP_OK:
                    handleParams.setOkHttpStatus(OkHttpStatus.SUCCESS);
                    if (handleParams.getParser() != null) {
                        try {
                            Object resultObject = handleParams.getParser().parse(msg);
//                            handleParams.setViewCacheBean((ViewCacheBean) resultObject);
                            handleParams.setResponseObj(resultObject);
                        } catch (Exception e) {
                            e.printStackTrace();
                            handleParams.setOkHttpStatus(OkHttpStatus.PARSE_ERROR);
                        }
                    } else {
                        handleParams.setResponse(msg);
                    }
                    break;
                case HttpURLConnection.HTTP_BAD_REQUEST:
                    Log.e(TAG, "------SC_BAD_REQUEST------");
                    handleParams.setOkHttpStatus(OkHttpStatus.BAD_REQUEST);
                    handleParams.setResponse(msg);
                    if (handleParams.getParser() != null) {
                        try {
                            Object resultModel = handleParams.getParser().parse(msg);
                            handleParams.setResponse(resultModel);
                        } catch (Exception e) {
                            e.printStackTrace();
                            handleParams.setOkHttpStatus(OkHttpStatus.PARSE_ERROR);
                        }
                    }
                    break;
                case HttpURLConnection.HTTP_UNAUTHORIZED:
                    handleParams.setOkHttpStatus(OkHttpStatus.UNAUTHORIZED);
                    Log.e(TAG, "------SC_UNAUTHORIZED------");
                    throw new IOException(OkHttpStatus.UNAUTHORIZED.toString());
                case HttpURLConnection.HTTP_MOVED_TEMP:
                    handleParams.setOkHttpStatus(OkHttpStatus.SC_MOVED_TEMPORARILY);
                    Log.e(TAG, "------SC_MOVED_TEMPORARILY------");
                    throw new IOException(OkHttpStatus.SC_MOVED_TEMPORARILY.toString());
                case HttpURLConnection.HTTP_NOT_FOUND:
                    handleParams.setOkHttpStatus(OkHttpStatus.NOT_FOUND);
                    Log.e(TAG, "------SC_NOT_FOUND------");
                    throw new IOException(OkHttpStatus.NOT_FOUND.toString());
                case HttpURLConnection.HTTP_CLIENT_TIMEOUT:
                    handleParams.setOkHttpStatus(OkHttpStatus.REQUEST_TIMEOUT);
                    Log.e(TAG, "------REQUEST_TIMEOUT------");
                    throw new IOException(OkHttpStatus.REQUEST_TIMEOUT.toString());
                case HttpURLConnection.HTTP_INTERNAL_ERROR:
                    handleParams.setOkHttpStatus(OkHttpStatus.INTERNAL_SERVER_ERROR);
                    Log.e(TAG, "------SC_INTERNAL_SERVER_ERROR------");
                    throw new IOException("zuihuibao is down. Try again later.");
                default:
                    handleParams.setOkHttpStatus(OkHttpStatus.OTHER_HTTPERROR);
                    throw new IOException("Error connecting to zuihuibao: " + statusCode + ". Try again later.");
            }
        } catch (IOException e) {
            e.printStackTrace();
            //现在失败的时候统一设置状态为 OkHttpStatus.OTHER_HTTPERROR ，如何想查看错误码，可以看log.e打印出来的信息
            handleParams.setOkHttpStatus(OkHttpStatus.OTHER_HTTPERROR);
        } finally {
            //在这里可以处理一些缓存的东西，可扩展
        }

    }

    /**
     * 请求执行结束后的自定义操作
     *
     * @param handleParams
     */
    protected void onExecuteFinished(ApiHandleParams handleParams) {
    }

    /**
     * send message to UI thread ,update view and others
     *
     * @param handleTask
     */
    private void notifyMainThread(ApiHandleParams handleTask) {
        if (handleTask.getApiHandler() != null) {
            Message message = handleTask.getApiHandler().obtainMessage();
            message.obj = handleTask;
            handleTask.getApiHandler().sendMessage(message);
        }
    }


    /**
     * 普通请求头信息+参数校验头信息
     *
     * @param valuePairs
     * @return
     */
    protected Headers addHttpHeader(List<NameValuePair> valuePairs) {
        Headers.Builder headers = new Headers.Builder();
        headers.add("Accept", "application/json");

        // 参数校验头信息
        if (valuePairs == null) {
            return headers.build();
        } else {
            List<String> nameParams = new ArrayList<>();
            for (NameValuePair nameValuePair : valuePairs) {
                if (nameValuePair.getName() != null && nameValuePair.getValue() != null) {
                    nameParams.add(nameValuePair.getName());
//					headers.add(nameValuePair.getName(), nameValuePair.getValue());
                }
            }
            Collections.sort(nameParams, new Comparator<String>() {

                @Override
                public int compare(String lhs, String rhs) {
                    if (lhs.compareTo(rhs) < 0) {
                        return -1;
                    } else {
                        return 1;
                    }
                }
            });
            StringBuilder stringBuilder = new StringBuilder();
//            stringBuilder.append(EncryptUtil.secretKey);
            for (String name : nameParams) {
                String value = headers.get(name);
                if (!TextUtils.isEmpty(value)) {
                    stringBuilder.append(name).append(value);
                }
            }
//            stringBuilder.append(EncryptUtil.iv);
//            headers.add("sign", org.apache.commons.codec.digest.DigestUtils.shaHex(stringBuilder.toString()).toUpperCase());
            return headers.build();
        }
    }

    protected Headers addHttpHeader() {
        Headers.Builder headers = new Headers.Builder();
        headers.add("Accept", "application/json");
        return headers.build();
    }

    protected Headers addHttpHeader(String cookie) {
        Headers.Builder headers = new Headers.Builder();
        headers.add("Accept", "application/json");
        if (!TextUtils.isEmpty(cookie)) {
            headers.add("Cookie", cookie);
        }
        return headers.build();
    }
    protected Headers addHttpHeaders(List<NameValuePair> headersValuePair) {
        Headers.Builder headers = new Headers.Builder();
        headers.add("Accept", "application/json");
        if (headersValuePair!=null){
            for (NameValuePair nameValuePair :headersValuePair){
                headers.add(nameValuePair.getName(),nameValuePair.getValue());
            }
        }
        return headers.build();
    }

    private String buildQueryUrl(String url ,List<NameValuePair> params) {
        String uri = url;
        StringBuilder queryBuilder = new StringBuilder(uri);
        if (!uri.contains("?")) {
            queryBuilder.append("?");
        } else if (!uri.endsWith("?")) {
            queryBuilder.append("&");
        }
        List<NameValuePair> queryParams = params;
        if (queryParams != null) {
            for (NameValuePair kv : queryParams) {
                String name = kv.getName();
                String value = kv.getValue();
                if (!TextUtils.isEmpty(name) && value != null) {
                    queryBuilder.append(name)
                            .append("=")
                            .append(value)
                            .append("&");
                }
            }
        }

        if (queryBuilder.charAt(queryBuilder.length() - 1) == '&') {
            queryBuilder.deleteCharAt(queryBuilder.length() - 1);
        }

        if (queryBuilder.charAt(queryBuilder.length() - 1) == '?') {
            queryBuilder.deleteCharAt(queryBuilder.length() - 1);
        }
        return queryBuilder.toString();
    }
    private String buildQueryUrl(OkRequestParams okRequestParams) {
        String uri = okRequestParams.getUrl();
        StringBuilder queryBuilder = new StringBuilder(uri);
        if (!uri.contains("?")) {
            queryBuilder.append("?");
        } else if (!uri.endsWith("?")) {
            queryBuilder.append("&");
        }
        List<NameValuePair> queryParams = okRequestParams.getParams();
        if (queryParams != null) {
            for (NameValuePair kv : queryParams) {
                String name = kv.getName();
                String value = kv.getValue();
                if (!TextUtils.isEmpty(name) && value != null) {
                    queryBuilder.append(name)
                            .append("=")
                            .append(value)
                            .append("&");
                }
            }
        }

        if (queryBuilder.charAt(queryBuilder.length() - 1) == '&') {
            queryBuilder.deleteCharAt(queryBuilder.length() - 1);
        }

        if (queryBuilder.charAt(queryBuilder.length() - 1) == '?') {
            queryBuilder.deleteCharAt(queryBuilder.length() - 1);
        }
        return queryBuilder.toString();
    }

    /**
     * 创建client_id
     *
     * @return
     */
    @SuppressLint("MissingPermission")
    public String getClientid() {
        // TODO Auto-generated method stub
        String clientIDFromClient = "";
        TelephonyManager telephonyManager = (TelephonyManager) MyApplication.getInstance().getSystemService(Context.TELEPHONY_SERVICE);
        if (telephonyManager != null) {
            clientIDFromClient = telephonyManager.getDeviceId();
        } else {
            clientIDFromClient = getWifiInfo();
        }
        if (TextUtils.isEmpty(clientIDFromClient)) {
            clientIDFromClient = "00000000000000000000";
        }
        return clientIDFromClient;
    }

    private String getWifiInfo() {

        String macAddress = "";
        // 先获取mac
        WifiManager wifi = (WifiManager) MyApplication.getInstance().getApplicationContext().getSystemService(Context.WIFI_SERVICE);
        /* 获取mac地址 */
        if (wifi != null) {
            WifiInfo info = wifi.getConnectionInfo();
            if (info != null && info.getMacAddress() != null) {
                macAddress = info.getMacAddress().replace(":", "");
            }
        }
        return macAddress;
    }

    /**
     * 根据请求tag取消请求
     *
     * @param requestTag
     */
    public void cancelRequestByTag(final String requestTag) {
        new Thread(new Runnable() {
            @Override
            public void run() {
//                mClient.cancel(requestTag);
            }
        }).start();
    }
}
